﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;
using System.Text;

namespace Fantacid303.ProcessAffinityControl.Common
{
	/// <summary>
	/// Helper class for processor affinity stuff
	/// </summary>
	public class ProcessorAffinityHelper
	{
		/// <summary>
		/// Gets the count of hyper threading processors in the system
		/// </summary>
		/// <value>The count of hyper threading processors.</value>
		public static uint HyperThreadingCores
		{ get; private set; }

		/// <summary>
		/// Gets the count of real processors in the system.
		/// </summary>
		/// <value>The count of real processors.</value>
		public static uint RealProcessorCores
		{ get; private set; }

		/// <summary>
		/// Gets the affinity mask for real processors.
		/// </summary>
		/// <value>The affinity mask for real processors.</value>
		public static ulong RealCoreProcessAffinity
		{ get; private set; }

		/// <summary>
		/// Gets the affinity mask for threading processors.
		/// </summary>
		/// <value>The the affinity mask for threading processors.</value>
		public static ulong HyperThreadingCoresProcessAffinity
		{ get; private set; }

		/// <summary>
		/// Gets the affinity for all processors in the system
		/// </summary>
		/// <value>The affinity for all processors.</value>
		public static ulong AllCoreAffinity
		{ get; private set; }

		/// <summary>
		/// Gets a value indicating whether the current system contains threading processors.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if the current system contains threading processors; otherwise, <c>false</c>.
		/// </value>
		public static bool HasThreadingCores
		{
			get { return HyperThreadingCores != 0; }
		}

		/// <summary>
		/// Initializes the <see cref="ProcessorAffinityHelper"/> class.
		/// </summary>
		static ProcessorAffinityHelper()
		{
			uint logicalProcessors = 0;
			foreach (var computer in new ManagementObjectSearcher("Select * from Win32_Processor").Get())
			{
				RealProcessorCores += (uint)computer["NumberOfCores"];
				logicalProcessors += (uint)computer["NumberOfLogicalProcessors"];
			}

			HyperThreadingCores = logicalProcessors - RealProcessorCores;

			/*
				CPUs
				
				0	1
				1	2
				2	4
				3	8
				-------
				4	16
				5	32
				6	64
				7	128
			
				SET Critical Process Affinity = 	(2^cores)-1
				SET normal process affinity=		(2^all CPUs)- (2^cores)
			 */

			RealCoreProcessAffinity = (ulong)Math.Pow(2, RealProcessorCores) - 1;
			HyperThreadingCoresProcessAffinity = (ulong)Math.Pow(2, RealProcessorCores + HyperThreadingCores) - (ulong)Math.Pow(2, RealProcessorCores);
			AllCoreAffinity = (ulong)Math.Pow(2, RealProcessorCores + HyperThreadingCores) - 1;
		}

		/// <summary>
		/// Converts an <see cref="ProcessorAffinityType"/> to a affinity mask
		/// </summary>
		/// <param name="affinityType">Type of the affinity.</param>
		/// <param name="mask">The mask.</param>
		/// <returns></returns>
		public static ulong VirtualAffinityMaskToReal(ProcessorAffinityType affinityType, ulong mask)
		{
			switch (affinityType)
			{
				case ProcessorAffinityType.AllCores:
					return ProcessorAffinityHelper.AllCoreAffinity;
				case ProcessorAffinityType.RealCores:
					return ProcessorAffinityHelper.RealCoreProcessAffinity;
				case ProcessorAffinityType.ThreadingCores:
					return ProcessorAffinityHelper.HyperThreadingCoresProcessAffinity;
				case ProcessorAffinityType.Custom:
					return mask;
				default:
					throw new NotImplementedException();
			}
		}
	}
}